package com.rwind.dynamic.service;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


/**
 *
 * @Service
 * public class RegisterBeansDynamically implements BeanFactoryAware {
 *
 *     private ConfigurableBeanFactory beanFactory;
 *
 *     public <T> void registerBean(String beanName, T bean) {
 *         beanFactory.registerSingleton(beanName, bean);
 *     }
 *
 *     @Override
 *     public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
 *         this.beanFactory = (ConfigurableBeanFactory) beanFactory;
 *     }
 * }
 *
 *  this.applicationContext = applicationContext;
 *         ConfigurableApplicationContext context = (ConfigurableApplicationContext)applicationContext;
 *         DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)context.getBeanFactory();
 *
 *         BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(YourClass.class);
 *
 *         beanDefinitionBuilder.addPropertyValue("property1", "propertyValue");
 *         beanDefinitionBuilder.addPropertyValue("property2", applicationContext.getBean(AnotherClass.class));
 *
 *
 *         beanFactory.registerBeanDefinition("yourBeanName", beanDefinitionBuilder.getBeanDefinition());
 *
 */
public class DynamicRegistUtil  {

    /**
     * 动态注册Bean
     *
     * @param beanName    bean名称
     * @param targetClass bean对应的类
     */
    public  static void registerBeanDefinition(String beanName, Class<?> targetClass) {
        //获取BeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory =
                (DefaultListableBeanFactory) SpringUtil.getApplicationContext().getAutowireCapableBeanFactory();
        //创建bean信息.
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(targetClass);
        //动态注册bean.
        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());


    }

    public static void registerDatasource(){
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.driverClassName("org.h2.Driver");
        dataSourceBuilder.url("jdbc:h2:mem:test");
        dataSourceBuilder.username("SA");
        dataSourceBuilder.password("");
        DataSource datasource = dataSourceBuilder.build();
        DruidDataSource druidDataSource = new DruidDataSource( ) ;

    }

    /**
     * 动态创建数据源
     * @param drive
     * @param url
     * @param username
     * @param password
     * @param datasourceName
     * @return
     * @throws Exception
     */
    public DataSource createdDataSource(String drive, String url, String username,
                                        String password, String datasourceName) throws Exception {
        Map<String,Object> map = new HashMap<>();
        map.put(DruidDataSourceFactory.PROP_DRIVERCLASSNAME, drive);
        map.put(DruidDataSourceFactory.PROP_URL, url);
        map.put(DruidDataSourceFactory.PROP_USERNAME, username);
        map.put(DruidDataSourceFactory.PROP_PASSWORD, password);
        // 初始化时建立物理连接的个数
        map.put(DruidDataSourceFactory.PROP_INITIALSIZE, "10");
        // 最小连接池数量
        map.put(DruidDataSourceFactory.PROP_MINIDLE, "10");
        // 最大连接池数量
        map.put(DruidDataSourceFactory.PROP_MAXACTIVE, "50");
        // 获取连接时最大等待时间，单位毫秒
        map.put(DruidDataSourceFactory.PROP_MAXWAIT, "60000");
        // 检测连接的间隔时间，单位毫秒
        map.put(DruidDataSourceFactory.PROP_TIMEBETWEENEVICTIONRUNSMILLIS, "60000");
        // wall:防御sql注入  stat:监控统计
        map.put(DruidDataSourceFactory.PROP_FILTERS, "wall,stat");
        map.put(DruidDataSourceFactory.PROP_NAME, datasourceName);
        return DruidDataSourceFactory.createDataSource(map);
    }

    /**
     * 动态卸载Bean
     *
     * @param beanName bean名称
     */
    public static void unRegisterBeanDefinition(String beanName) {
       ApplicationContext applicationContext = SpringUtil.getApplicationContext();
        if (!applicationContext.containsBean(beanName)) {
            return;
        }
        //获取BeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory =
                (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        defaultListableBeanFactory.removeBeanDefinition(beanName);
    }


    /**
     * 动态注册Controller
     *
     * @param controllerBeanName controller的beanName
     * @throws Exception 反射异常
     */
    public static void registerController(String controllerBeanName)
            throws Exception {
        final RequestMappingHandlerMapping requestMappingHandlerMapping =
                SpringUtil.getBean(RequestMappingHandlerMapping.class);
        if (requestMappingHandlerMapping != null) {
            Object controller = SpringUtil.getBean(controllerBeanName);
            if (controller == null) {
                return;
            }
            //注册Controller
            Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().
                    getDeclaredMethod("detectHandlerMethods", Object.class);
            //将private改为可使用
            method.setAccessible(true);
            method.invoke(requestMappingHandlerMapping, controllerBeanName);
        }
    }

    /**
     * 动态去掉Controller的Mapping
     *
     * @param controllerBeanName controller的beanName
     */
    public  static void unregisterController(String controllerBeanName) {
        final RequestMappingHandlerMapping requestMappingHandlerMapping
                = SpringUtil.getBean(RequestMappingHandlerMapping.class);
        if (requestMappingHandlerMapping != null) {
            Object controller = SpringUtil.getBean(controllerBeanName);
            if (controller == null) {
                return;
            }
            final Class<?> targetClass = controller.getClass();
            ReflectionUtils.doWithMethods(targetClass, method -> {
                Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                try {
                    Method createMappingMethod = RequestMappingHandlerMapping.class.
                            getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
                    createMappingMethod.setAccessible(true);
                    RequestMappingInfo requestMappingInfo = (RequestMappingInfo)
                            createMappingMethod.invoke(requestMappingHandlerMapping, specificMethod, targetClass);
                    if (requestMappingInfo != null) {
                        requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, ReflectionUtils.USER_DECLARED_METHODS);
        }
    }

}